package proxy;

import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.ArrayDeque;
import java.util.Date;
import java.util.Queue;

public final class Log {
	
	public static enum Mode {
		INFO, DEBUG, WARN, ERROR
	}
	
	private static Mode mode = Mode.INFO;
	
	private static boolean append = false;
	
	private PrintWriter out;
	
	private static String charset = "UTF-8";
	
	private Queue<Thread> threads = new ArrayDeque<Thread>(0);
	
	private Object lock = new Object();
	
	private Log(String name){
		try {
			out = new PrintWriter(new OutputStreamWriter(new FileOutputStream(name, append), charset));
			new Thread(){
				public void run(){
					while(true){
						while(!threads.isEmpty()){
							Thread thread = threads.poll();
							thread.start();
							try {
								thread.join();
							} catch (InterruptedException e) {
								e.printStackTrace();
							}
						}
						try {
							synchronized (lock) {
								lock.wait();
							}
						} catch (InterruptedException e) {
						}
					}
				}
			}.start();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public static Log getLog(String name){
		return new Log(name);
	}
	
	public static void setMode(Mode mode){
		Log.mode = mode;
	}
	
	public static void setCharset(String charset){
		Log.charset = charset;
	}
	
	public void error(Exception e){
		write(Mode.ERROR, null, e);
	}
	
	public void error(String message){
		write(Mode.ERROR, message, null);
	}
	
	public void error(String message, Exception e){
		write(Mode.ERROR, message, e);
	}
	
	public void debug(Exception e){
		write(Mode.DEBUG, null, e);
	}
	
	public void debug(String message){
		write(Mode.DEBUG, message, null);
	}
	
	public void debug(String message, Exception e){
		write(Mode.DEBUG, message, e);
	}
	
	public void warn(Exception e){
		write(Mode.WARN, null, e);
	}
	
	public void warn(String message){
		write(Mode.WARN, message, null);
	}
	
	public void warn(String message, Exception e){
		write(Mode.WARN, message, e);
	}
	
	public void info(Exception e){
		write(Mode.INFO, null, e);
	}
	
	public void info(String message){
		write(Mode.INFO, message, null);
	}
	
	public void info(String message, Exception e){
		write(Mode.INFO, message, e);
	}
	
	private void write(Mode mode, final String message, final Exception e){
		if (!Log.mode.equals(mode) || out == null) return;
		threads.add(new Thread(){
			public void run(){
				if (message != null) {
					out.println();
					out.print(new Date().toString());
					out.print(" : ");
					out.print(message);
				}
				if (e != null){
					out.println();
					e.printStackTrace(out);
				}
				out.flush();
			}
		});
		synchronized (lock) {
			lock.notifyAll();
		}
	}
}
